Conversation
# Why Android biometric prompts default to requiring an explicit confirmation after a successful passive biometric match. Some applications need the platform-supported implicit-authentication path while retaining `BIOMETRIC_STRONG`, the existing `CryptoObject`, and authenticated keystore access. # How Adds an Android-only `requireConfirmation` SecureStore option, defaulting to `true`. The option is applied only when constructing the current `BiometricPrompt`; it is not persisted and does not change key aliases, key generation, authentication strength, invalidation, or iOS behavior. Reads use the stored authentication requirement together with the current call's confirmation preference. The native-component fixture exposes the option for both sync and async reads/writes. # Test Plan Automated: - `pnpm exec turbo build --filter=expo-secure-store` - `pnpm test` in `packages/expo-secure-store` (Android and iOS; 37 tests / 25 snapshots) - `pnpm typecheck` in `packages/expo-secure-store` - `pnpm lint` in `packages/expo-secure-store` - `pnpm exec et check-packages expo-secure-store` - `./gradlew :expo-secure-store:testDebugUnitTest` in `apps/bare-expo/android` Manual fixture coverage is available in the SecureStore native-component screen: enable authentication and compare `requireConfirmation` enabled/disabled for sync and async set/get on an Android device with passive biometrics. Android may override implicit authentication based on device or policy. On iOS the option is accepted but ignored, preserving the existing Keychain prompt behavior. # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [x] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) Co-authored-by: Brent Vatne <brentvatne@gmail.com>
Co-authored-by: Łukasz Kosmaty <kosmatylukasz@gmail.com>
# Why There are multiple warnings which pollute the test output in router # How 1. Solve suspense warnings by using `renderAsync`/`renderHookAsync` function from `testing-library` 2. Ignore `LogBoxNotificationContainer` warning 3. Add `renderRouterAsync` utility # Test Plan CI # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) --- <sub>Stack created with <a href="https://github.com/github/gh-stack">GitHub Stacks CLI</a> • <a href="https://gh.io/stacks-feedback">Give Feedback 💬</a></sub>
…48943) > [!WARNING] > **Agent-authored and NOT human-reviewed.** An automated `/verify --fix` run for #48902 wrote this change and checked it in a sandbox; the reasoning and evidence are in the outcome comment on that issue. Review it as you would any external contribution. Requested by @brentvatne · [investigation run](https://github.com/expo/expo/actions/runs/31815585821) · refs #48902 ## Why Reported in [#48902](#48902). The universal `BottomSheet` wraps `children` in a container of its own on every platform and hardcodes 16 units of padding on it, and `BottomSheetProps` has no field for that container — so sheet content can never reach the sheet's edges. No full-bleed row, image, divider or list separator is possible. The `modifiers` escape hatch does not give it back. On Android `modifiers` goes to `ModalBottomSheet`, not to the padded `Column` that holds the children ([index.android.tsx#L63](https://github.com/expo/expo/blob/bc467bc47f2ae58f54e75caa1363c4b59b3b38a5/packages/expo-ui/src/universal/BottomSheet/index.android.tsx#L63-L63)). On iOS it lands on the same `Group` but is appended *after* the hardcoded entry, so a second `padding` stacks onto the first instead of replacing it ([index.ios.tsx#L28-L38](https://github.com/expo/expo/blob/bc467bc47f2ae58f54e75caa1363c4b59b3b38a5/packages/expo-ui/src/universal/BottomSheet/index.ios.tsx#L28-L38)). Web never reads `modifiers` at all, and its inner `div` carries `padding: 16` ([index.tsx#L101-L104](https://github.com/expo/expo/blob/bc467bc47f2ae58f54e75caa1363c4b59b3b38a5/packages/expo-ui/src/universal/BottomSheet/index.tsx#L101-L104)). ## How Adds one optional prop, `contentPadding?: number | { top?, bottom?, left?, right? }`, applied to the content container on all three platforms. A shared `resolveContentPadding` helper resolves it against the inset each platform applies today, so **when the prop is omitted nothing changes**: iOS keeps `{ top: 16, leading: 16, trailing: 16 }`, Android keeps `padding(16, showDragIndicator ? 0 : 16, 16, 0)`, web keeps `padding: 16`. This is deliberately not a change to any default — existing sheets render identically, and `contentPadding={0}` is what unlocks full-bleed content. Per-edge values follow the universal layer's style-like naming (`left`/`right`, as `ScrollView` already maps `paddingLeft` → `leading`), and an edge left out of the object is `0`, so `contentPadding` fully owns the container's padding rather than merging with the platform default. That is a deliberate semantic and worth a reviewer's eye: on web, whose default bottom inset is 16, `contentPadding={{ left: 0 }}` therefore clears the bottom inset too (measured, table below). Docs data was regenerated with `et gdad -p expo-ui/universal/bottomsheet`. ## Test Plan Repo tooling, in a full monorepo checkout at `bc467bc` with `pnpm install`: ``` packages/expo-ui $ pnpm run typecheck # clean packages/expo-ui $ pnpm run lint --max-warnings 0 # Found 0 warnings and 0 errors. packages/expo-ui $ pnpm test # Test Suites: 28 passed, Tests: 133 passed ``` Behavior, using the reporter's repro (`kilarsky/expo-ui-bottom-sheet-content-padding-repro`, `@expo/ui` 57.0.10, `expo` 57.0.12) with the change applied to the installed package and bundled by Metro: **iOS** — hosted iPhone simulator, Expo Go, SDK 57: | Arm | Result | | --- | --- | | Unpatched, no prop | blue bar inset 16pt from both sheet edges (the bug) | | Patched, prop omitted | inset unchanged — no default changed | | Patched, `contentPadding={0}` | bar spans the sheet edge to edge | | Patched, `contentPadding={{ top: 8, left: 40, right: 40 }}` | 40pt side inset, 8pt above the bar | The "prop omitted" arm is the guard against changing a default, and the code makes the same point more strongly than a screenshot can: iOS resolves to `{ top: 16, bottom: 0, leading: 16, trailing: 16 }`, and `PaddingModifier` already maps the previously-omitted `bottom` to `0` (`packages/expo-ui/ios/Modifiers/ViewModifierRegistry.swift`), giving identical `EdgeInsets`; Android's resolved call expands to literally the previous `padding(16, showDragIndicator ? 0 : 16, 16, 0)`. **Web** — the same repro served by Metro and measured in headless Chrome (`getBoundingClientRect` on the bar against the sheet, plus the content container's computed padding): | Arm | Content container padding | Bar gap left / right | | --- | --- | --- | | Unpatched, no prop | `16px 16px 16px 16px` | 16 / 16 | | Patched, prop omitted | `16px 16px 16px 16px` | 16 / 16 | | Patched, `contentPadding={0}` | `0px 0px 0px 0px` | 0 / 0 | | Patched, `contentPadding={{ left: 0 }}` | `0px 0px 0px 0px` | 0 / 0 | **Android** — not exercised on a device. An emulator session was started for this run and never became available, so the Android arm rests on the shared, unit-tested resolver and on code symmetry with the two arms that were measured; a reviewer with an Android device should confirm `contentPadding={0}` and the `showDragIndicator={false}` default there. Note also that Compose's `Modifier.padding` rejects negative values, so a negative `contentPadding` — which iOS and web accept — would throw on Android; the change does not validate it. ## Checklist - [x] `CHANGELOG.md` entry added. - [x] Type-checks, lints and tests via the package's own scripts in a real monorepo checkout. - [x] Documentation updated (`bottomsheet.mdx` usage section + regenerated API data). --------- Co-authored-by: expo-bot <expo-bot@users.noreply.github.com> Co-authored-by: nishan (o^▽^o) <nishanbende@gmail.com>
# Why Fingerprint can load different env files for the same project when `NODE_ENV` changes. # How I updated Fingerprint to use the shared API and set the mode to `development` before loading Expo config and env files. # Test Plan Tests and CI checks pass. # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
…ameraView (#49028) # Why Mounting and unmounting `CameraView` a few times freezes the iOS UI for tens of seconds. Reported in #48780 with a repro at https://github.com/nrgbistro/expo-camera-simulator-freeze-repro. This is a regression from #44159, which replaced the compile-time `#if !targetEnvironment(simulator)` guards with a runtime device check. Two paths were left unguarded, and both end up waiting on a capture graph that cannot start. When the graph never starts, every later rebuild of it waits out its own ~9s AVFoundation deadline. Nothing detaches the preview layer on teardown, so `AVCaptureVideoPreviewLayer` does it in `dealloc`, on the main thread, against a session still marked running. That is the stack in the report: ``` CA::Transaction::commit NSKVODeallocate AVCaptureVideoPreviewLayer dealloc AVCaptureSession commitConfiguration AVCaptureSession _buildAndRunGraph AVRunLoopCondition _waitInMode ``` Several wedged sessions accumulate, and the waits add up into a freeze. # How `stopSession()` now stops the session before it removes inputs and outputs, instead of deconfiguring a live session and stopping it afterwards. The stop also moved above the device-availability guard, which previously returned early and left the session running. `removeFromSuperview()` captures the session manager and the preview layer strongly, and detaches the layer on the main thread once the session has stopped. It captured `self` weakly before, so teardown could be skipped entirely if React Native released the view first. `updateCameraIsActive()` checks `hasAvailableCameraDevice` before starting, which is the call site #44159 missed. `startSession()` and `stopSession()` already had that check. # Test Plan Added two native unit tests in `packages/expo-camera/ios/Tests/`, run with `et native-unit-tests --packages expo-camera`. Both fail on `main` and pass here. The teardown test measures how long the main thread blocks while the preview layer is released, which is the symptom users see. Measured the freeze itself on a simulator with no capture device, over 8 mount/unmount cycles: 75.5s wall and 71.4s of main-runloop overrun before, against 4.1s and 18ms after.
We keep receiving feedback messages like "init", "connect", "submit" as agents and humans test the command structure. This PR updates submit-expo-feedback to prefer an explicit --message/-m and keep the positional message argument with a deprecated warning. It also limits the feedback string to be at least 40 characters so we reduce as many of these mistake submissions as possible. Also updates agent-cli-detector to 0.1.6 to detect opencode and muse code.
…firmationDialog and Popover (#48949) # Why Follow-up to #48904, where @brentvatne spotted that `Background` declared a `modifiers` prop but never forwarded it. The same bug is in `main` for four more components: `Overlay`, `Alert`, `ConfirmationDialog` and `Popover`. Every modifier passed to them is silently ignored. # How All four destructured `modifiers` out of the props and used it only as the source of event listeners – `createViewModifierEventListener` returns just `{ onGlobalEvent }`, so the array itself never reached the native view. They now pass it on, the same way `Shapes`, `Button` and `List` already do. The native side needed no changes: their props inherit the `modifiers` field from `UIBaseViewProps`, and `ExpoUIView` wraps each view in `UIBaseView`, which calls `applyModifiers`. # Test Plan Verified in bare-expo on the iOS simulator: temporarily added `modifiers={[opacity(0.4)]}` to the `Overlay` in the native-component-list screen (not part of this PR) and confirmed the whole composition — the card and the NEW badge – renders translucent. Before the fix the modifier had no effect. `et check-packages @expo/ui` passes. | Before | After | | --- | --- | | <img width="300" alt="Simulator Screenshot - iPhone 17 Pro - 2026-08-15 at 01 58 02" src="https://github.com/user-attachments/assets/60adc458-64eb-4b8f-b5be-2ef7669c8dce" /> | <img width="300" alt="Simulator Screenshot - iPhone 17 Pro - 2026-08-15 at 01 57 53" src="https://github.com/user-attachments/assets/8687cbe0-3fae-4e7b-b491-4ff461f70d46" /> | # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources - [ ] This diff will work correctly for `npx expo prebuild` & EAS Build - [x] Conforms with the Documentation Writing Style Guide
# Why
`padding()` without arguments applies SwiftUI's system default padding
to all
edges, but specifying any edge dropped the rest to zero. There was no
way to
express "system default on top, 24 points on the sides" — SwiftUI writes
that
as `.padding(.top).padding(.horizontal, 24)`.
# How
Each edge of the modifier now accepts `number | 'auto'`. Values are
decoded by
a `PaddingValue` convertible (`ios/Convertibles/PaddingValue.swift`)
with
`.auto` and `.points` cases; anything that is neither a number nor
`'auto'`
throws with a message naming the accepted values.
`EdgeInsets` can only carry lengths, so edges resolved to `auto` are
collected
into an `Edge.Set` and applied by a second `padding(_:)` call — padding
modifiers add up and the two calls cover disjoint edges. The system
default has
no fixed length (it adapts to size class and Dynamic Type), so it cannot
be
resolved to a number on either side.
Existing behaviour is unchanged: a specific edge still overrides the
shorthand
covering it, unspecified edges still get no padding, and `padding()`
with no
parameters still pads every edge by the system default.
# Test Plan
Added a "Padding" section to the modifiers screen in
native-component-list
covering `padding()`, `padding({ top: 'auto', horizontal: 24 })` and
`padding({ all: 'auto', leading: 0 })`, and verified it in bare-expo on
the iOS
simulator: the auto edges match the system default of the plain
`padding()` row,
while the explicit edges keep their exact lengths.
`et check-packages @expo/ui` passes and the app builds (`BUILD
SUCCEEDED`).
# Checklist
- [x] I added a `changelog.md` entry and rebuilt the package sources
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
- [x] Conforms with the Documentation Writing Style Guide
---------
Co-authored-by: nishan (o^▽^o) <nishanbende@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )